package cn.cs.common;

import cn.cs.pojo.ClientUser;
import cn.cs.pojo.RepairUser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cz.mallat.uasparser.UserAgentInfo;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class TokenUtils {
    @Resource
    private RedisUtils redisUtils;

    public final static int SESSION_TIMEOUT=2*60*60;//默认2h
    /**
     * 置换保护时间
     */
    public final static int REPLACEMENT_PROTECTION_TIMEOUT=60*60;//默认1h
    /**
     * 旧token延迟过期时间
     */
    public final static int REPLACEMENT_DELAY=2*60;//默认2min
    private int expire = SESSION_TIMEOUT;// 2h
    private String tokenPrefix = "token:";//统一加入 token前缀标识

    public int getExpire() {
        return expire;
    }

    public void setExpire(int expire) {
        this.expire = expire;
    }

    /***
     * 验证token是否能用
     * @param agent Http头中的user-agent信息
     * @param
     * @return Token格式<br/>
     * 	PC：“前缀PC-USERCODE-USERID-CREATIONDATE-RONDEM[6位]”
     * 	PC-zhangsan-111-20190220111111-212232
     *  <br/>
     *  Android：“前缀ANDROID-USERCODE-USERID-CREATIONDATE-RONDEM[6位]”
     */
    public Map<String,Object> checkToken(String agent, String token,String code){
        Map<String,Object> map = new HashMap<String, Object>();
        if(this.exists(token)){
            boolean bo = this.validateMore(agent,token);
            if(!bo){
//                token = this.replaceToken(agent,token);
                map.put("errorCode","1");
//                map.put("token",token);
            }else{
             String tokenStr =    redisUtils.get(token).toString();
                Map<String,Object> jsMap =(Map<String,Object>)JSONObject.parse(tokenStr);
                String deveCode = jsMap.get("devoCode").toString();
                if(deveCode.equals(code)){
                    map.put("errorCode","2");
                }else{
                    map.put("errorCode","0");
                    map.put("errorMsg","其他设备已经登录，请重新登录");
                }
            }
        }else{
            map.put("errorCode","0");
            map.put("errorMsg","此token不存在");
        }
        return map;
    }


    /***
     * @param agent Http头中的user-agent信息
     * @param user 用户信息
     * @return Token格式<br/>
     * 	PC：“前缀PC-USERCODE-USERID-CREATIONDATE-RONDEM[6位]”
     * 	PC-zhangsan-111-20190220111111-212232
     *  <br/>
     *  Android：“前缀ANDROID-USERCODE-USERID-CREATIONDATE-RONDEM[6位]”
     */
    public String generateToken(String agent, RepairUser user) {
        // TODO Auto-generated method stub
        try {
            UserAgentInfo userAgentInfo = UserAgentUtil.getUasParser().parse(
                    agent);
            StringBuilder sb = new StringBuilder();
            sb.append(tokenPrefix);//统一前缀
            String agentStr = "";
            if (userAgentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN)) {
                if (UserAgentUtil.CheckAgent(agent)) {
                    sb.append("MOBILE-");
                    agentStr = "MOBILE";
                } else {
                    sb.append("PC-");
                    agentStr = "PC";
                }
            } else if (userAgentInfo.getDeviceType()
                    .equals("Personal computer")) {
                sb.append("PC-");
                agentStr = "PC";
            } else{
                agentStr = "MOBILE";
                sb.append("MOBILE-");
            }
//			sb.append(user.getUserCode() + "-");
            sb.append(MD5.getMd5(user.getPhone(),32) + "-");//加密用户名称
            sb.append(user.getId() + "-");
            sb.append(MD5.getMd5(user.getId(), 6)
                    + "-");
            sb.append(MD5.getMd5(agentStr, 6));// 识别客户端的简化实现——6位MD5码

            return sb.toString();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
    public String generateToken(String agent, Map<String,Object> user) {
        // TODO Auto-generated method stub
        try {
            UserAgentInfo userAgentInfo = UserAgentUtil.getUasParser().parse(
                    agent);
            StringBuilder sb = new StringBuilder();
            sb.append(tokenPrefix);//统一前缀
            String agentStr = "";
            if (userAgentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN)) {
                if (UserAgentUtil.CheckAgent(agent)) {
                    sb.append("MOBILE-");
                    agentStr = "MOBILE";
                } else {
                    sb.append("PC-");
                    agentStr = "PC";
                }
            } else if (userAgentInfo.getDeviceType()
                    .equals("Personal computer")) {
                sb.append("PC-");
                agentStr = "PC";
            } else{
                agentStr = "MOBILE";
                sb.append("MOBILE-");
            }
//			sb.append(user.getUserCode() + "-");
            sb.append(MD5.getMd5(user.get("phone").toString(),32) + "-");//加密用户名称
            sb.append(user.get("id") + "-");
            sb.append(MD5.getMd5(user.get("id").toString(), 6)
                    + "-");
            sb.append(MD5.getMd5(agentStr, 6));// 识别客户端的简化实现——6位MD5码

            return sb.toString();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public String generateToken(String agent, ClientUser user) {
        // TODO Auto-generated method stub
        try {
            UserAgentInfo userAgentInfo = UserAgentUtil.getUasParser().parse(
                    agent);
            StringBuilder sb = new StringBuilder();
            sb.append(tokenPrefix);//统一前缀
            if (userAgentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN)) {
                if (UserAgentUtil.CheckAgent(agent)) {
                    sb.append("MOBILE-");
                } else {
                    sb.append("PC-");
                }
            } else if (userAgentInfo.getDeviceType()
                    .equals("Personal computer")) {
                sb.append("PC-");
            } else
                sb.append("MOBILE-");
//			sb.append(user.getUserCode() + "-");
            sb.append(MD5.getMd5(user.getOpenId(),32) + "-");//加密用户名称
            sb.append(user.getId() + "-");
            sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
                    + "-");
            sb.append(MD5.getMd5(agent, 6));// 识别客户端的简化实现——6位MD5码

            return sb.toString();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public void save(String token, RepairUser user) {
        if (token.startsWith(tokenPrefix+"PC-"))
            redisUtils.set(token, expire, JSON.toJSONString(user));
        else
            redisUtils.set(token, JSON.toJSONString(user));// 手机认证信息永不失效
    }

    public void save(String token, Map<String,Object> user) {
        if (token.startsWith(tokenPrefix+"PC-"))
            redisUtils.set(token, expire, JSON.toJSONString(user));
        else
            redisUtils.set(token, JSON.toJSONString(user));// 手机认证信息永不失效
    }

    public void save(String token, ClientUser user) {
        if (token.startsWith(tokenPrefix+"PC-"))
            redisUtils.set(token, expire, JSON.toJSONString(user));
        else
            redisUtils.set(token, JSON.toJSONString(user));// 手机认证信息永不失效
    }




    public RepairUser load(String token) {
        return JSON.parseObject(redisUtils.get(token).toString(), RepairUser.class);
    }

    public void delete(String token) {
        if (redisUtils.exist(token))
            redisUtils.delete(token);
    }

    private boolean exists(String token) {
        return redisUtils.exist(token);
    }

    public String replaceToken(String agent, String token)
            throws TokenValidationFailedException {
        // 验证旧token是否有效
        if (!exists(token)) {// token不存在
            throw new TokenValidationFailedException("未知的token或 token已过期");// 终止置换
        }
//        Date TokenGenTime;// token生成时间
//        try {
//            String[] tokenDetails = token.split("-");
//            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
//            TokenGenTime = formatter.parse(tokenDetails[3]);
//        } catch ( Exception e) {
//            // TODO Auto-generated catch block
//            throw new TokenValidationFailedException("token格式错误:" + token);
//        }

//        long passed = Calendar.getInstance().getTimeInMillis()
//                - TokenGenTime.getTime();// token已产生时间
//        if (passed < REPLACEMENT_PROTECTION_TIMEOUT * 1000) {// 置换保护期内
//            throw new TokenValidationFailedException("token处于置换保护期内，剩余"
//                    + (REPLACEMENT_PROTECTION_TIMEOUT * 1000 - passed) / 1000
//                    + "(s),禁止置换");
//        }
        // 置换token
        String newToken = "";
        RepairUser user = this.load(token);
        long ttl = redisUtils.getExpire(token);// token有效期（剩余秒数 ）
        if (ttl > 0 || ttl == -1) {// 兼容手机与PC端的token在有效期
            newToken = this.generateToken(agent, user);
            redisUtils.delete(token);
            this.save(newToken, user);// 缓存新token

//            redisUtils.set(token, this.REPLACEMENT_DELAY,
//                    JSON.toJSONString(user));// 2分钟后旧token过期，注意手机端由永久有效变为2分钟（REPLACEMENT_DELAY默认值）后失效
        } else {// 其它未考虑情况，不予置换
            throw new TokenValidationFailedException("当前token的过期时间异常,禁止置换");
        }
        return newToken;
    }


    public String replaceTokenClientUser(String agent, String token)
            throws TokenValidationFailedException {
        // 验证旧token是否有效
        if (!exists(token)) {// token不存在
            throw new TokenValidationFailedException("未知的token或 token已过期");// 终止置换
        }

        // 置换token
        String newToken = "";
        ClientUser user = JSON.parseObject(redisUtils.get(token).toString(), ClientUser.class);
        long ttl = redisUtils.getExpire(token);// token有效期（剩余秒数 ）
        if (ttl > 0 || ttl == -1) {// 兼容手机与PC端的token在有效期
            newToken = this.generateToken(agent, user);
            redisUtils.delete(token);
            this.save(newToken, user);// 缓存新token

        } else {// 其它未考虑情况，不予置换
            throw new TokenValidationFailedException("当前token的过期时间异常,禁止置换");
        }
        return newToken;
    }

    public boolean validate(String agent, String token) {
        if (!exists(token)) {// token不存在
            return false;
        }
        try {
            Date TokenGenTime;// token生成时间
            String agentMD5;
            String[] tokenDetails = token.split("-");
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
            TokenGenTime = formatter.parse(tokenDetails[3]);
            long passed = Calendar.getInstance().getTimeInMillis()
                    - TokenGenTime.getTime();
            if(passed>this.expire*1000)
                return false;
            agentMD5 = tokenDetails[4];
            if(MD5.getMd5(agent, 6).equals(agentMD5))
                return true;
        } catch (ParseException e) {
            return false;
        }
        return false;
    }
    public boolean validateMore(String agent, String token) {
        try {
            Date TokenGenTime;// token生成时间
            String agentMD5;
            String[] tokenDetails = token.split("-");
//            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
//            TokenGenTime = formatter.parse(tokenDetails[3]);
//            long passed = Calendar.getInstance().getTimeInMillis()
//                    - TokenGenTime.getTime();
//            if(passed>this.expire*1000)
//                return false;
//            agentMD5 = tokenDetails[4];
//            if(MD5.getMd5(agent, 6).equals(agentMD5))
                return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
//        return false;
    }

}
